Skip to content

feat(models): migrate Models APIs to typed NemoClient - #803

Draft
maxdubrinsky wants to merge 10 commits into
mainfrom
aircore-876-migrate-models-to-nemoclient/md
Draft

feat(models): migrate Models APIs to typed NemoClient#803
maxdubrinsky wants to merge 10 commits into
mainfrom
aircore-876-migrate-models-to-nemoclient/md

Conversation

@maxdubrinsky

@maxdubrinsky maxdubrinsky commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add typed sync and async Models clients covering the 42 genuine Models CRUD/status/version operations across models, adapters, providers, prompts, deployments, and deployment configs.
  • Replace the Stainless-backed packages/models resource implementation while preserving existing URL helpers, polling behavior, pagination, conflict handling, and delete status visibility; sync its vendored package copy without regenerating Stainless.
  • Migrate the Models controller, reconcilers, sidecars, model-spec task, workspace cleanup, customization/Data Designer/Automodel/Guardrails/platform-seed/RL/Unsloth consumers, and their tests.
  • Preserve the wrapped Stainless SDK retry contract in client_from_platform, including explicit retry headers, 408/409/429, server errors, and reasonable Retry-After values, without changing standalone NemoClient defaults.
  • Preserve complete provider metadata during replacement PUTs and omit unrelated partial-update fields so reconciliation cannot clear user-selected routing configuration.

Validation

  • 178 client/foundation tests passed.
  • 1,205 Models unit tests passed; 1 torch-dependent test skipped.
  • 251 downstream consumer tests passed; 1 optional training-image test skipped.
  • 49 nemo-unsloth plugin tests passed against typed Models/Files mocks.
  • Ruff and formatting passed across all non-generated changed Python files.
  • Focused ty, vendored-SDK/CLI sync lints, negative Stainless searches, warning-strict controller checks, and git diff --check passed.
  • Multiple independent adversarial review rounds found no remaining blocker or high-severity issue.

Intentional boundaries

  • Inference Gateway provider discovery remains on its IGW proxy operation.
  • VirtualModel CRUD is owned by AIRCORE-877.
  • Files list/download operations and their retry/error handling remain on Files clients or the existing SDK where not part of Models.
  • OpenAI proxy, readiness, SSE, and raw-byte operations are not modeled as Models CRUD endpoints.

Follow-up

Directly owned NemoClient transport teardown is tracked separately in AIRCORE-933.

Linear: https://linear.app/nvidia/issue/AIRCORE-876

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@github-actions github-actions Bot added the feat label Jul 20, 2026
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 29022/36949 78.5% 62.6%
Integration Tests 17404/35667 48.8% 20.8%

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Main dropped the legacy docker and k8s-nim-operator backends (#705) and
replaced the reconcilers' per-entity Model Entity reads and writes with
ModelEntityCache (#953, #951). The branch's changes to the deleted backends
were import rewrites, so they go with the files. Everywhere the cache
supersedes a direct read/write, main's semantics win and the calls it makes
are routed through the typed client.

ModelEntityCache arrived on main built on the umbrella SDK, which left
models_controller holding a dangling self._models_sdk after the merge. It is
migrated to AsyncModelsClient here, since leaving it on the SDK would make
the branch's premise only half true.

_load_virtual_models still reached for self._models_sdk. Its bare
except Exception reported the AttributeError as a VirtualModel listing
failure and skipped orphan cleanup, so the tests stayed green while the
cleanup silently never ran.

VirtualModel work stays on the umbrella SDK: it is an inference-gateway
resource, not a Models one. The SDK exception types are aliased to say so.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Every test added here was checked by mutation: reverting the behaviour it
describes fails that test and only that test.

Model Entity cache. The migration moved writes behind request bodies and
responses behind .data(), and nothing asserted either. Dropping .data() or
the write-back into the snapshot left the suite green, while in production
it would cost a create/409 round trip for the second change to an entity in
a phase. The create body also never carried staged field updates in any
test, though that is the only shape the provider reconciler produces.

VirtualModel cleanup. VirtualModel.name is optional on the wire, and the
guard for it was the untested one of the pair; without it the delete goes
out as name=None.

_retrieve_model_entity. The cache branch is what runs in production, since
the cache is refreshed at the start of every phase, yet only the direct
fetch was covered, and then only because those tests never run a phase. A
miss now reports the entity as absent with no fallback, which is worth
stating outright.

known_deployment_ids skips contexts with no deployment. That was a merge
resolution, and reading it unguarded stalls the controller on every tick
rather than degrading one pass.

_load_virtual_models catches APIError rather than Exception. "The listing
failed" should mean the service, not a bug in this method; catching
everything is what let an AttributeError disable orphan cleanup silently.

method() had no test at all for class-level access, which is what
Mock(spec=...) and every introspection tool performs. Its dynamic
attributes are now declared on the descriptor, and the comment no longer
claims inspect.signature resolves through __wrapped__, because it does not:
signature() refuses a non-callable before it looks.

make_models_client keyed off name prefixes, which made the three sync
OpenAI route builders awaitable and gave the two unpaginated list endpoints
a paginated response. It now reads the method() descriptors and their
declared return types.

Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant